/*
>> PixelLookupOp.s
>>
>> Pixel lookup operation skeleton, allowing buffer and scalar inputs,
>> buffer outputs, and lookup buffer inputs.
>>
>> Author: Wouter Caarls
>> Date:   14-06-2006
*/

#include "ilpstrategies.h"
#include "kernelsplit.h"
#include "pixelbypixel.h"

stratego code makeabsolute(code param, int stride, code body)
{
  strategies
    main = ?(param, stride, body); !(body, (param, stride));
    try(env-alltd(MakeAbsolute)); bottomup(try(apply-conc))

  rules
    MakeAbsolute:
      (ArrayIndex(ArrayIndex(p, i1), i2), (p, s)) ->
      c:exp |[ ~exp:p [ ~exp:i1 * ~exp:s + ~exp:i2] ]|

    apply-conc:
      Conc(list1, list2) -> <conc>(list1, list2)
}

void PixelLookupOp(argcode_t *argcode, int arguments, code body)
{
  int ii, stride, lines, read=0, readagain;
  code splitbody[];

  for (ii=0; ii < arguments; ii++)
  {
    argcode[ii].state.buffer = 1;
    argcode[ii].state.delay = 0;
    argcode[ii].state.state = 0;

    /* Allocate arguments */
    if (argcode[ii].argtype == ARG_STREAM_IN || argcode[ii].argtype == ARG_STREAM_OUT)
    {
      body = replace(`*$@argcode[ii].id`, argcode[ii].id, body);
      @declare(argcode[ii].datatype, argcode[ii].id);
    }
    else if (argcode[ii].argtype == ARG_LOOKUP)
    {
      argcode[ii].state.state = 1024*1024;

      @declare(addrpointer(argcode[ii].datatype), argcode[ii].id);
      stride = BufferGetStride(ii);
      lines = BufferGetLines(ii);

      @argcode[ii].id = @cast(addrpointer(argcode[ii].datatype),
                          malloc(stride*lines*sizeof(argcode[ii].datatype)));

      if (!argcode[ii].extents[1].size)
        body = makeabsolute(argcode[ii].id, stride, body);
    }
    else
    {   
      /* Non-streams must be initialised */
      @declare(addrpointer(argcode[ii].datatype), argcode[ii].id);
      if (@dataclass(argcode[ii].datatype) == DATACLASS_INTEGER)   
        @argcode[ii].id = &(@argcode[ii].argid).data.d;
      else if (@dataclass(argcode[ii].datatype) == DATACLASS_FLOAT)
        @argcode[ii].id = &(@argcode[ii].argid).data.f;
       else
        @argcode[ii].id = @cast(addpointer(argcode[ii].datatype),
                                (@argcode[ii].argid).data.b);
    }
  }  

  splitbody = @kernelsplit(body);

  /* Load lookup tables */
  do
  {
    readagain = 0;

    for (ii=0; ii < arguments; ii++)
      if (argcode[ii].argtype == ARG_LOOKUP)
      {
        int readnow;
        @declare(addpointer(argcode[ii].datatype), `ptr`);

        readnow = BufferPeek(ii, (void*)&ptr, stride*sizeof(argcode[ii].datatype),
                             SCU_BUF_BLOCK_ALL);
        memcpy((void*)&(@argcode[ii].id)[read], (void*)ptr, readnow);
        BufferReleasePeeked(ii, stride*sizeof(argcode[ii].datatype));

        if (readnow == stride * sizeof(argcode[ii].datatype))
          readagain = 1;
      }

    read = read + stride;
  } while (readagain);
     
  /* Finalize lookup table streams */
  for (ii=0; ii < arguments; ii++)
    if (argcode[ii].argtype == ARG_LOOKUP)
      BufferFinalize(ii);

  prologue {
    $@splitbody[0];
  };

  repeat {
    $@splitbody[1];
  };

  epilogue {
   finalizearguments(argcode, arguments);
   $@splitbody[2];
  };
}
